import katex from 'katex';

function isValidDelim(state, delim) {
  const max = state.posMax;
  const can_open = !!(delim.can_open || delim.length === 1);
  const can_close = !!(delim.can_close || delim.length === 1);

  if (!can_open && !can_close) {
    return false;
  }

  const nextChar = state.src[delim.end];
  const prevChar = state.src[delim.start - 1];

  if (prevChar === delim.marker && nextChar === delim.marker) {
    return false;
  }

  if (nextChar === delim.marker && can_open) {
    return false;
  }

  if (prevChar === delim.marker && can_close) {
    return false;
  }

  return true;
}

function katexInline(latex) {
  try {
    return katex.renderToString(latex, {
      displayMode: false,
      throwOnError: false,
      output: 'html',
      strict: false,
      trust: true,
      macros: {
        '\\RR': '\\mathbb{R}',
        '\\NN': '\\mathbb{N}',
        '\\ZZ': '\\mathbb{Z}'
      },
      strict: false,
      trust: true,
      maxSize: 500,
      maxExpand: 1000,
      fleqn: false,
      output: 'html',
      throwOnError: false,
      errorColor: '#cc0000',
      minRuleThickness: 0.05,
      sizeIndex: 0,
      fallback: {
        font: 'KaTeX_Main,Times New Roman,serif',
        css: {
          '-ms-high-contrast-adjust': 'none',
          '-webkit-font-smoothing': 'antialiased',
          'text-rendering': 'optimizeLegibility'
        }
      }
    });
  } catch (error) {
    console.error(`KaTeX inline rendering error for formula: ${latex}\nError: ${error.message}`);
    return `<span class="katex-error" title="${error.message}" data-latex="${latex.replace(/"/g, '&quot;')}">${latex}</span>`;
  }
}

function katexBlock(latex) {
  try {
    return katex.renderToString(latex, {
      displayMode: true,
      throwOnError: false,
      output: 'html',
      strict: false,
      trust: true,
      macros: {
        '\\RR': '\\mathbb{R}',
        '\\NN': '\\mathbb{N}',
        '\\ZZ': '\\mathbb{Z}'
      }
    });
  } catch (error) {
    console.error(`KaTeX block rendering error for formula: ${latex}\nError: ${error.message}`);
    return `<div class="katex-error" title="${error.message}" data-latex="${latex.replace(/"/g, '&quot;')}">${latex}</div>`;
  }
}

function math_inline(state, silent) {
  let pos = state.pos;
  const ch = state.src.charAt(pos);

  if (ch !== '$') { return false; }
  if (silent) { return false; }

  const start = pos;
  pos++;
  const max = state.posMax;

  while (pos < max && state.src.charAt(pos) !== '$') {
    if (state.src.charAt(pos) === '\\' && pos + 1 < max) {
      pos += 2;
    } else {
      pos++;
    }
  }

  if (pos >= max || state.src.charAt(pos) !== '$') {
    state.pos = start;
    return false;
  }

  const latex = state.src.slice(start + 1, pos);
  const token = state.push('math_inline', 'math', 0);
  token.markup = '$';
  token.content = latex;
  token.meta = { latex };

  state.pos = pos + 1;
  return true;
}

function math_block(state, startLine, endLine, silent) {
  let pos = state.bMarks[startLine] + state.tShift[startLine];
  let max = state.eMarks[startLine];

  if (pos + 2 > max) { return false; }

  const marker = state.src.slice(pos, pos + 2);

  if (marker !== '$$') { return false; }

  pos += 2;
  let mem = pos;
  let nextLine = startLine;

  while (nextLine < endLine) {
    nextLine++;
    if (nextLine >= endLine) { break; }

    pos = state.bMarks[nextLine] + state.tShift[nextLine];
    max = state.eMarks[nextLine];

    if (pos < max && state.tShift[nextLine] < state.blkIndent) { break; }
    if (state.src.slice(pos, pos + 2) === '$$') {
      pos += 2;
      break;
    }
  }

  if (nextLine >= endLine) { return false; }
  if (silent) { return true; }

  const content = state.src.slice(mem, state.bMarks[nextLine]).trim();
  state.line = nextLine + 1;

  const token = state.push('math_block', 'math', 0);
  token.block = true;
  token.content = content;
  token.meta = { latex: content };
  token.map = [startLine, state.line];
  token.markup = '$$';

  return true;
}

export default function math_plugin(md, options) {
  options = options || {};

  md.inline.ruler.before('escape', 'math_inline', math_inline);
  md.block.ruler.before('blockquote', 'math_block', math_block, {
    alt: ['paragraph', 'reference', 'blockquote', 'list']
  });

  md.renderer.rules.math_inline = function(tokens, idx) {
    return katexInline(tokens[idx].content);
  };

  md.renderer.rules.math_block = function(tokens, idx) {
    return katexBlock(tokens[idx].content);
  };
}
